"use strict";

const NUMS = "0123456789";

var toolkit = require("./toolkit");

class SudokuInternal {
    constructor() {
        this.matrix = (function(m) {
            for (var i = 0; i < 9; i++) {
                m[i] = [0, 0, 0, 0, 0, 0, 0, 0, 0];
            }
            return m;
        })(new Array(9));

        this.orders = (function(m) {
            function random() {
                var row = [0, 1, 2, 3, 4, 5, 6, 7, 8];
                for (var i = 0; i < 9; i++) {
                    var b = ~~(Math.random() * 9);
                    var t = row[i];
                    row[i] = row[b];
                    row[b] = t;
                }
                return row;
            }

            for (var i = 0; i < 9; i++) {
                m[i] = random();
            }
            return m;
        })(new Array(9));
    }

    generate() {
        for (var i = 0; i < 9; i++) {
            if (!this.fillNumber(i)) {
                return false;
            }
        }
        return true;
    }

    fillNumber(index) {
        var n = index + 1;
        return this.fillRow(n, 0);
    }

    fillRow(n, rowIndex) {
        if (rowIndex >= 9) {
            return true;
        }

        // 按顺序找到当前行未填的空格
        var row = this.matrix[rowIndex];
        var orders = this.orders[rowIndex];
        for (var i = 0; i < orders.length; i++) {
            var colIndex = orders[i];
            if (row[colIndex]) {
                continue;
            }

            if (!this.check(n, rowIndex, colIndex)) {
                continue;
            }

            row[colIndex] = n;
            if (!this.fillRow(n, rowIndex + 1)) {
                row[colIndex] = 0;
                continue;
            }

            return true;
        }

        return false;
    }

    check(n, rowIndex, colIndex) {
        var block = toolkit.getBlockByCoor(this.matrix, rowIndex, colIndex);
        for (var i = 0; i < 9; i++) {
            if (this.matrix[i][colIndex] === n
                || this.matrix[rowIndex][i] === n
                || block[i] === n) {
                return false;
            }
        }

        return true;
    }
}

const __ = "__";

class Sudoku {
    constructor() {
        this[__] = new SudokuInternal();
    }

    get matrix() {
        return this[__].matrix;
    }

    print(matrix) {
        toolkit.print(matrix || this.matrix);
    }

    generate() {
        while (!this[__].generate()) {
            this[__] = new SudokuInternal();
        }
        return this;
    }

    make(level) {
        level = level || "normal";

        if (typeof level === "string") {
            switch (level.trim().toLowerCase()) {
                case "hard":
                    level = 9;
                    break;
                case "easy":
                    level = 1;
                    break;
                default:
                    level = 5;
                    break;
            }
        }

        level = Math.max(level, 3);
        level = Math.min(level, 6);

        return this.matrix.map(function(row) {
            return row.map(function(v) {
                return Math.random() * 9 < level ? 0 : v;
            });
        });
    }
}

module.exports = Sudoku;
